﻿/**
*┌──────────────────────────────────────────────────────────────┐
*│　描    述：HPSocket的TCPServer、TCPClient通讯相关的工具类（类库提供的方法、属性学习）
*│　作    者：执笔小白
*│　版    本：1.0
*│　创建时间：2023-6-13 10:40:56
*└──────────────────────────────────────────────────────────────┘
*┌──────────────────────────────────────────────────────────────┐
*│　命名空间: ZhibiXiaobai
*│　类    名：HPSocket_TCPServerHelper、HPSocket_TcpClientHelper
*└──────────────────────────────────────────────────────────────┘
*/
using HPSocket;
using HPSocket.Base;
using HPSocket.Sdk;
using HPSocket.Tcp;
using System;
using System.Collections.Concurrent;
using System.Linq;
using System.Net;
using System.Text;
using System.Threading.Tasks;

namespace csharp_networkprotocol_hpsocket
{
    /// <summary>
    /// HPSocket_TCPServer示例帮助类（类库提供的方法、属性学习）
    /// System.Net.Sockets类库
    /// TcpServer
    /// </summary>
    public class HPSocket_TCPServerHelper
    {
        #region 接收端（服务端）
        /// <summary>
        /// 侦听来自 TCP 网络客户端的连接
        /// </summary>
        public ITcpServer _server = null;
        #region 创建时的设置项
        /// <summary>
        /// 要绑定的服务器地址
        /// </summary>
        public string Address
        {
            get => _server.Address;
            set => _server.Address = value;
        }

        /// <summary>
        /// 要绑定的服务器端口
        /// </summary>
        public ushort Port
        {
            get => _server.Port;
            set => _server.Port = value;
        }

        /// <summary>
        /// 设置最大连接数（组件会根据设置值预分配内存，因此需要根据实际情况设置，不宜过大）
        /// </summary>
        public uint MaxConnectionCount
        {
            get => _server.MaxConnectionCount;
            set => _server.MaxConnectionCount = value;
        }

        /// <summary>
        /// 读取或设置是否标记静默时间
        /// （设置为 true 时 DisconnectSilenceConnections() 和 GetSilencePeriod()才有效，默认：false）
        /// </summary>   
        public bool IsMarkSilence
        {
            get => _server.IsMarkSilence;
            set => _server.IsMarkSilence = value;
        }

        /// <summary>
        /// 获取或设置数据发送策略
        /// </summary>
        public SendPolicy SendPolicy
        {
            get => _server.SendPolicy;
            set => _server.SendPolicy = value;
        }

        /// <summary>
        /// 获取或设置 OnSend 事件同步策略
        /// </summary>
        public OnSendSyncPolicy OnSendSyncPolicy
        {
            get => _server.OnSendSyncPolicy;
            set => _server.OnSendSyncPolicy = value;
        }

        /// <summary>
        /// 获取或设置地址重用选项
        /// </summary>
        public ReuseAddressPolicy ReuseAddressPolicy
        {
            get => _server.ReuseAddressPolicy;
            set => _server.ReuseAddressPolicy = value;
        }

        /// <summary>
        /// 获取或设置是否开启 nodelay 模式 (默认: false, 不开启)
        /// </summary>
        public bool NoDelay
        {
            get => _server.NoDelay;
            set => _server.NoDelay = value;
        }

        #region 心跳检测
        /// <summary>
        /// 读取或设置心跳包间隔（毫秒，0 则不发送心跳包）
        /// </summary>
        public uint KeepAliveTime
        {
            get => _server.KeepAliveTime;
            set => _server.KeepAliveTime = value;
        }


        /// <summary>
        /// 读取或设置心跳确认包检测间隔（毫秒，0 不发送心跳包，如果超过若干次 [默认：WinXP 5 次, Win7 10 次] 检测不到心跳确认包则认为已断线）
        /// </summary>
        public uint KeepAliveInterval
        {
            get => _server.KeepAliveInterval;
            set => _server.KeepAliveInterval = value;
        }

        #endregion 心跳检测

        #region 性能优化
        /// <summary>
        /// 读取或设置 Accept 预投递数量（根据负载调整设置，Accept 预投递数量越大则支持的并发连接请求越多）
        /// </summary>
        public uint AcceptSocketCount
        {
            get => _server.AcceptSocketCount;
            set => _server.AcceptSocketCount = value;
        }


        /// <summary>
        /// 读取或设置通信数据缓冲区大小（根据平均通信数据包大小调整设置，通常设置为 1024 的倍数）
        /// </summary>
        public uint SocketBufferSize
        {
            get => _server.SocketBufferSize;
            set => _server.SocketBufferSize = value;
        }


        /// <summary>
        /// 读取或设置监听 Socket 的等候队列大小（根据并发连接数量调整设置）
        /// </summary>
        public uint SocketListenQueue
        {
            get => _server.SocketListenQueue;
            set => _server.SocketListenQueue = value;
        }


        /// <summary>
        /// 读取或设置工作线程数量（通常设置为 2 * CPU + 2）
        /// </summary>
        public uint WorkerThreadCount
        {
            get => _server.WorkerThreadCount;
            set => _server.WorkerThreadCount = value;
        }


        /// <summary>
        /// 读取或设置 Socket 缓存对象锁定时间（毫秒，在锁定期间该 Socket 缓存对象不能被获取使用）
        /// </summary>
        public uint FreeSocketObjLockTime
        {
            get => _server.FreeSocketObjLockTime;
            set => _server.FreeSocketObjLockTime = value;
        }


        /// <summary>
        /// 读取或设置 Socket 缓存池大小（通常设置为平均并发连接数量的 1/3 - 1/2）
        /// </summary>
        public uint FreeSocketObjPool
        {
            get => _server.FreeSocketObjPool;
            set => _server.FreeSocketObjPool = value;
        }


        /// <summary>
        /// 读取或设置内存块缓存池大小（通常设置为 Socket 缓存池大小的 2 - 3 倍）
        /// </summary>
        public uint FreeBufferObjPool
        {
            get => _server.FreeBufferObjPool;
            set => _server.FreeBufferObjPool = value;
        }

        /// <summary>
        /// 读取或设置内存块缓存池大小（通常设置为 Socket 缓存池大小的 2 - 3 倍）
        /// </summary>
        public uint FreeSocketObjHold
        {
            get => _server.FreeSocketObjHold;
            set => _server.FreeSocketObjHold = value;
        }

        /// <summary>
        /// 读取或设置内存块缓存池回收阀值（通常设置为内存块缓存池大小的 3 倍）
        /// </summary>
        public uint FreeBufferObjHold
        {
            get => _server.FreeBufferObjHold;
            set => _server.FreeBufferObjHold = value;
        }

        #endregion 性能优化
        #endregion 创建时的设置项

        #region 常用属性
        /// <summary>
        /// 获取是否启动
        /// </summary>
        public bool HasStarted => _server.HasStarted;

        /// <summary>
        /// 获取状态
        /// </summary>
        public ServiceState State => _server.State;

        /// <summary>
        /// 获取连接数
        /// </summary>
        public uint ConnectionCount => _server.ConnectionCount;

        /// <summary>
        /// 是否为安全连接（SSL/HTTPS）
        /// </summary>
        public bool IsSecure => _server.IsSecure;
        #endregion 常用属性

        #region 常用方法
        /// <summary>
        /// 启动服务
        /// </summary>
        /// <returns></returns>
        public bool Start() => _server.Start();

        /// <summary>
        /// 停止服务
        /// </summary>
        /// <returns></returns>
        public bool Stop() => _server.Stop();

        #region 附加信息
        /// <summary>
        /// 获取所有附加数据
        /// </summary>
        /// <returns></returns>
        public ConcurrentDictionary<IntPtr, object> GetAllExtra() => _server.GetAllExtra();

        /// <summary>
        /// 获取附加数据
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="connId"></param>
        /// <returns></returns>
        public T GetExtra<T>(IntPtr connId) => _server.GetExtra<T>(connId);

        /// <summary>
        /// 获取连接附加数据, 非托管版本, hp-socket自带方法；非特殊需求不要使用这个方法, 请直接使用 GetExtra();
        /// </summary>
        /// <param name="connId"></param>
        /// <param name="extra"></param>
        /// <returns></returns>
        public bool NativeGetConnectionExtra(IntPtr connId, out IntPtr extra) => _server.NativeGetConnectionExtra(connId, out extra);

        /// <summary>
        /// 设置附加数据
        /// </summary>
        /// <param name="connId"></param>
        /// <param name="obj"></param>
        /// <returns></returns>
        public bool SetExtra(IntPtr connId, object obj) => _server.SetExtra(connId, obj);

        /// <summary>
        /// 设置连接附加数据, 非托管版本, hp-socket自带方法；非特殊需求不要使用这个方法, 请直接使用 SetExtra();
        /// </summary>
        /// <param name="connId"></param>
        /// <param name="extra"></param>
        /// <returns></returns>
        public bool NativeSetConnectionExtra(IntPtr connId, IntPtr extra) => _server.NativeSetConnectionExtra(connId, extra);

        /// <summary>
        /// 删除附加数据
        /// </summary>
        /// <param name="connId"></param>
        /// <returns></returns>
        public bool RemoveExtra(IntPtr connId) => _server.RemoveExtra(connId);
        #endregion 附加信息

        #region 发送数据
        /// <summary>
        /// 发送数据
        /// </summary>
        /// <param name="connId">连接id</param>
        /// <param name="bytes">数据源</param>
        /// <param name="length">数据长度</param>
        /// <returns></returns>
        public bool Send(IntPtr connId, byte[] bytes, int length) => _server.Send(connId, bytes, length);

        /// <summary>
        /// 发送数据-指针偏移
        /// </summary>
        /// <param name="connId"></param>
        /// <param name="bytes"></param>
        /// <param name="offset">对bytes的偏移</param>
        /// <param name="length">发多大</param>
        /// <returns></returns>
        public bool Send(IntPtr connId, byte[] bytes, int offset, int length) => _server.Send(connId, bytes, offset, length);
        /// <summary>
        /// 发送多组数据 向指定连接发送多组数据 TCP - 顺序发送所有数据包
        /// </summary>
        /// <param name="connId">连接 ID</param>
        /// <param name="buffers">发送缓冲区数组</param>
        /// <returns>true.成功,false.失败，可通过 SYSGetLastError() 获取 Windows 错误代码</returns>
        public bool SendPackets(IntPtr connId, Wsabuf[] buffers) => SendPackets(connId, buffers);
        #endregion 发送数据

        #region 发送本地小文件
        /// <summary>
        /// 发送本地小文件
        /// 向指定连接发送 4096 KB 以下的小文件
        /// </summary>
        /// <param name="connId"></param>
        /// <param name="filePath">文件路径</param>
        /// <param name="head">头部附加数据</param>
        /// <param name="tail">尾部附加数据</param>
        /// <returns>true.成功,false.失败，可通过 SYSGetLastError() 获取 Windows 错误代码</returns>
        public bool SendSmallFile(IntPtr connId, string filePath, ref Wsabuf head, ref Wsabuf tail) => _server.SendSmallFile(connId, filePath, ref head, ref tail);

        /// <summary>
        /// 发送本地小文件
        /// 向指定连接发送 4096 KB 以下的小文件
        /// </summary>
        /// <param name="connId"></param>
        /// <param name="filePath">文件路径</param>
        /// <param name="head">头部附加数据,可以为null</param>
        /// <param name="tail">尾部附加数据,可以为null</param>
        /// <returns>true.成功,false.失败，可通过 SYSGetLastError() 获取 Windows 错误代码</returns>
        public bool SendSmallFile(IntPtr connId, string filePath, byte[] head, byte[] tail) => _server.SendSmallFile(connId, filePath, head, tail);
        #endregion 发送本地小文件

        #region 查询服务器信息
        /// <summary>
        /// 获取监听socket的地址信息
        /// </summary>
        /// <param name="ip"></param>
        /// <param name="port"></param>
        /// <returns></returns>
        public bool GetListenAddress(out string ip, out ushort port) => _server.GetListenAddress(out ip, out port);
        #endregion 查询服务器信息

        #region 查询客户端信息
        /// <summary>
        /// 获取所有连接
        /// </summary>
        /// <returns></returns>
        public List<IntPtr> GetAllConnectionIds() => _server.GetAllConnectionIds();

        /// <summary>
        /// 获取某客户端连接是否有效
        /// </summary>
        /// <param name="connId"></param>
        /// <returns></returns>
        public bool IsConnected(IntPtr connId) => _server.IsConnected(connId);

        /// <summary>
        /// 获取某客户端连接的接收状态
        /// </summary>
        /// <param name="connId"></param>
        /// <returns></returns>
        public ReceiveState GetReceiveState(IntPtr connId) => _server.GetReceiveState(connId);

        /// <summary>
        /// 获取某个连接的本地地址信息
        /// </summary>
        /// <param name="connId"></param>
        /// <param name="ip"></param>
        /// <param name="port"></param>
        /// <returns></returns>
        public bool GetLocalAddress(IntPtr connId, out string ip, out ushort port) => _server.GetLocalAddress(connId, out ip, out port);

        /// <summary>
        /// 获取某个连接的远程地址信息
        /// </summary>
        /// <param name="connId"></param>
        /// <param name="ip"></param>
        /// <param name="port"></param>
        /// <returns></returns>
        public bool GetRemoteAddress(IntPtr connId, out string ip, out ushort port) => _server.GetRemoteAddress(connId, out ip, out port);

        /// <summary>
        /// 获取指定连接的连接时长（毫秒）
        /// </summary>
        /// <param name="connId"></param>
        /// <param name="period"></param>
        /// <returns></returns>
        public bool GetConnectPeriod(IntPtr connId, out uint period) => _server.GetConnectPeriod(connId, out period);

        /// <summary>
        /// 获取某个连接静默时间（毫秒）
        /// </summary>
        /// <param name="connId"></param>
        /// <param name="period"></param>
        /// <returns></returns>
        public bool GetSilencePeriod(IntPtr connId, out uint period) => _server.GetSilencePeriod(connId, out period);

        /// <summary>
        /// 获取连接中未发出数据的长度
        /// </summary>
        /// <param name="connId"></param>
        /// <param name="length"></param>
        /// <returns></returns>
        public bool GetPendingDataLength(IntPtr connId, out int length) => _server.GetPendingDataLength(connId, out length);

        #endregion 查询客户端信息

        #region 断开与客户端的连接
        /// <summary>
        /// 断开与某个客户的连接
        /// </summary>
        /// <param name="connId"></param>
        /// <param name="force">是否强制断开</param>
        /// <returns></returns>
        public bool Disconnect(IntPtr connId, bool force = true) => _server.Disconnect(connId, force);

        /// <summary>
        /// 断开超过指定时间的连接
        /// </summary>
        /// <param name="period">毫秒</param>
        /// <param name="force">强制</param>
        /// <returns></returns>
        public bool DisconnectLongConnections(uint period, bool force = true) => _server.DisconnectLongConnections(period, force);

        /// <summary>
        /// 断开超过指定时长的静默连接
        /// </summary>
        /// <param name="period">毫秒</param>
        /// <param name="force">强制</param>
        /// <returns></returns>
        public bool DisconnectSilenceConnections(uint period, bool force = true) => _server.DisconnectSilenceConnections(period, force);
        #endregion 断开与客户端的连接

        #region 暂停/唤醒接收某客户端
        /// <summary>
        /// 暂停接收
        /// </summary>
        /// <param name="connId"></param>
        /// <returns></returns>
        public bool PauseReceive(IntPtr connId) => _server.PauseReceive(connId);

        /// <summary>
        /// 唤醒接收
        /// </summary>
        /// <param name="connId"></param>
        /// <returns></returns>
        public bool ResumeReceive(IntPtr connId) => _server.ResumeReceive(connId);
        #endregion 暂停/唤醒接收某客户端
        #endregion 常用方法

        #region 创建侦听的方法与示例方法(后使用Start()开启侦听)
        /// <summary>
        /// 创建侦听
        /// </summary>
        /// <param name="localAddr">地址</param>
        /// <param name="port">端口</param>
        /// <returns></returns>
        public ITcpServer CreateTcpServer()
        {
            _server = new TcpServer();
            return _server;
        }
        /// <summary>
        /// 创建TCP侦听 -示例
        /// </summary>
        /// <param name="localAddr">地址</param>
        /// <param name="port">端口</param>
        /// <returns></returns>
        public ITcpServer CreateTcpServerDemo(IPAddress localAddr, int port)
        {
            // <1> 创建服务器对象
            _server = new TcpServer();

            // <2> 设置socket接收长度
            _server.SocketBufferSize = 4096;  // 4K
            _server.Address = "192.168.10.11";
            _server.Port = 8085;

            // <3> 绑定事件
            //event ServerAcceptEventHandler OnAccept;                // TCP连接准备事件
            //event ServerHandShakeEventHandler OnHandShake;          // TCP握手成功事件
            //event ServerPrepareListenEventHandler OnPrepareListen;  // 监听事件
            //event ServerSendEventHandler OnSend;                    // 数据包发送事件
            //event ServerReceiveEventHandler OnReceive;              // 数据包到达事件
            //event ServerCloseEventHandler OnClose;                  // TCP连接关闭事件
            //event ServerShutdownEventHandler OnShutdown;            // TCP服务器关闭事件

            _server.OnAccept += OnAccept;                // TCP连接准备事件-使用附加数据处理黏包
            _server.OnHandShake += OnHandShake;          // TCP握手成功事件
            _server.OnPrepareListen += OnPrepareListen;  // 监听事件
            _server.OnSend += OnSend;                    // 数据包发送事件
            _server.OnReceive += OnReceive;              // 数据包接收事件
            _server.OnClose += OnClose;                  // TCP连接关闭事件
            _server.OnShutdown += OnShutdown;            // TCP服务器关闭事件
            return _server;
        }

        #region TCP事件
        /// <summary>
        /// TCP连接事件（连接前）-使用附加数据处理黏包，不可异步
        /// </summary>
        /// <param name="sender">服务器对象</param>
        /// <param name="connId">连接ID</param>
        /// <param name="client">如果为 TCP 连接，pClient为 SOCKET 句柄；如果为 UDP 连接，pClient为 SOCKADDR 指针；</param>
        /// <returns></returns>
        private HandleResult OnAccept(IServer sender, IntPtr connId, IntPtr client)
        {
            // 获取客户端地址
            if (!sender.GetRemoteAddress(connId, out string ip, out ushort port))
            {
                return HandleResult.Error;
            }

            // 设置附加数据（用来做粘包处理）
            sender.SetExtra(connId, string.Empty);  // 初始化附加信息

            //ShowLog(string.Format("TCP客户端接入({0}), ip: {1}, port: {2}", connId, ip, port));
            return HandleResult.Ok;
        }

        /// <summary>
        /// TCP握手成功事件
        /// </summary>
        /// <param name="sender">服务器对象</param>
        /// <param name="connId">连接ID</param>
        /// <returns></returns>
        private HandleResult OnHandShake(IServer sender, IntPtr connId)
        {
            // 一般用不到
            return HandleResult.Ok;
        }

        /// <summary>
        /// 监听事件
        /// </summary>
        /// <param name="server">服务器对象</param>
        /// <param name="intPtr">连接ID</param>
        /// <returns></returns>
        private HandleResult OnPrepareListen(IServer server, IntPtr intPtr)
        {
            //ShowLog(string.Format("TCP服务器开启监听({0}:{1}), listen:{2}", server.Address, server.Port, intPtr));
            return HandleResult.Ok;
        }

        /// <summary>
        /// 数据包发送事件
        /// </summary>
        /// <param name="sender">服务器对象</param>
        /// <param name="connId">连接ID</param>
        /// <param name="data">数据包</param>
        /// <returns></returns>
        private HandleResult OnSend(IServer sender, IntPtr connId, byte[] data)
        {
            sender.GetRemoteAddress(connId, out string ip, out ushort port);  // 获取客户端地址

            //ShowLog(string.Format("TCP服务器发送数据[ID:{0},客户端地址：‘{1}:{2}’];数据[长度{3}]：{4}", connId, ip ?? "未找到IP", port, data.Length, Encoding.ASCII.GetString(data)));
            return HandleResult.Ok;
        }

        /// <summary>
        /// 数据包接收事件
        /// </summary>
        /// <param name="sender">服务器对象</param>
        /// <param name="connId">连接ID</param>
        /// <param name="data">数据包</param>
        /// <returns></returns>
        private HandleResult OnReceive(IServer sender, IntPtr connId, byte[] data)
        {
            // <1> 获取客户端地址
            if (!sender.GetRemoteAddress(connId, out string ip, out ushort port))
            {
                return HandleResult.Error;
            }
            // <2> 获取附加数据对象
            var extraDataStr = sender.GetExtra<string>(connId);
            if (extraDataStr == null)
            {
                return HandleResult.Error;
            }
            // <3> 将接收数据转换成字符串
            string msg = Encoding.ASCII.GetString(data);
            extraDataStr += msg;  // 添加数据到缓存_不合格的数据添加到缓存区（用于粘包、拆包）

            // <4> 显示信息
            //ShowLog(string.Format("TCP服务器接收客户端[ID:{0},IP:‘{1}:{2}’]的信息;数据:[长度{3}]: {4}", connId, ip, port, data.Length, msg));

            // <5> 处理数据
            HandleResult result;

            string _endsWith = "\r\n";  // TCP包的数据结束符

            while (true)
            {
                int index = extraDataStr.IndexOf(_endsWith);
                if (index == -1) // 数据接收不完整，忽略后等待下一次接收
                {
                    result = HandleResult.Ignore;
                    break;
                }
                else
                {
                    string validMsg = extraDataStr.Remove(0, index);
                    // <6> 业务处理-异步(validMsg内容解析时的格式校验属于业务范畴)

                    // <7> 移除已取出数据
                    extraDataStr = extraDataStr.Remove(0, index + _endsWith.Length);
                }
            }

            // <8> 保存PacketData数据
            if (extraDataStr.Length > _server.SocketBufferSize)  // 可选-控制长度为4096(注：实际所占控件大小>4096)
            {
                int length_Delete = extraDataStr.Length - Convert.ToInt32(_server.SocketBufferSize);
                extraDataStr = extraDataStr.Remove(0, length_Delete);  // 清除长度超标数据
            }
            sender.SetExtra(connId, extraDataStr);  // 初始化附加信息

            return result;
        }

        /// <summary>
        /// TCP连接关闭事件
        /// </summary>
        /// <param name="sender">服务器对象</param>
        /// <param name="connId">连接ID</param>
        /// <param name="socketOperation">关闭的类型</param>
        /// <param name="errorCode">错误时的错误代码</param>
        /// <returns></returns>
        private HandleResult OnClose(IServer sender, IntPtr connId, SocketOperation socketOperation, int errorCode)
        {
            // <1> 获取客户端地址
            if (!sender.GetRemoteAddress(connId, out string ip, out ushort port))
            {
                return HandleResult.Ignore;
            }
            // <2> 释放附加数据
            if (sender.GetExtra<string>(connId) != null)
            {
                sender.RemoveExtra(connId);  // 删除附加数据
            }
            // <3> 显示信息
            //ShowLog(string.Format("TCP客户端连接断开(ID {0},地址 {1}:{2}), 断开类型: {3},错误代码: {4}", connId, ip, port, socketOperation.ToString(), errorCode));

            return HandleResult.Ok;
        }

        /// <summary>
        /// TCP服务器关闭事件
        /// </summary>
        /// <param name="sender">服务器对象</param>
        /// <returns></returns>
        private HandleResult OnShutdown(IServer sender)
        {
            //ShowLog(string.Format("TCP服务器关闭({0}:{1})", sender.Address, sender.Port));
            return HandleResult.Ok;
        }
        #endregion TCP事件
        #endregion 创建侦听的方法与示例方法(后使用Start()开启侦听)
        #endregion 接收端（服务端）
    }

    /// <summary>
    /// HPSocket_TcpClient示例帮助类（示例学习）
    /// System.Net.Sockets类库
    /// TcpClient
    /// </summary>
    public class HPSocket_TcpClientHelper
    {
        #region 发送端（客户端）
        /// <summary>
        /// TCP客户端
        /// </summary>
        public ITcpClient _client = new TcpClient();

        /// <summary>
        /// 本地绑定到哪个ip
        /// </summary>
        public string BindAddress
        {
            get => _client.BindAddress;
            set => _client.BindAddress = value;
        }
        /// <summary>
        /// 本地绑定到哪个端口
        /// </summary>
        public ushort BindPort
        {
            get => _client.BindPort;
            set => _client.BindPort = value;
        }

        /// <summary>
        /// 远程服务器地址
        /// </summary>
        public string Address
        {
            get => _client.Address;
            set => _client.Address = value;
        }
        /// <summary>
        /// 远程服务器端口
        /// </summary>
        public ushort Port
        {
            get => _client.Port;
            set => _client.Port = value;
        }

        #region 创建时的设置项
        /// <summary>
        /// 是否异步连接，默认为真
        /// </summary>
        public bool Async
        {
            get => _client.Async;
            set => _client.Async = value;
        }

        /// <summary>
        /// 读取或设置通信数据缓冲区大小（根据平均通信数据包大小调整设置，通常设置为：(N * 1024) - sizeof(TBufferObj)）
        /// </summary>
        public uint SocketBufferSize
        {
            get => _client.SocketBufferSize;
            set => _client.SocketBufferSize = value;
        }

        /// <summary>
        /// 连接超时时间, 默认操作系统默认值
        /// 单位: 毫秒
        /// 异常:T:System.InvalidOperationException:同步连接、.NET Framework2.0以及设置小于100毫秒会引发此异常
        /// </summary>
        public int ConnectionTimeout
        {
            get => _client.ConnectionTimeout;
            set => _client.ConnectionTimeout = value;
        }

        /// <summary>
        /// 附加数据
        /// 赋值：client.ExtraData = myObj;
        /// 取值：var data = ExtraData as MyData;
        /// </summary>
        public object ExtraData
        {
            get => _client.ExtraData;
            set => _client.ExtraData = value;
        }

        /// <summary>
        /// 获取或设置是否开启 nodelay 模式 (默认: false, 不开启)
        /// </summary>
        public bool NoDelay
        {
            get => _client.NoDelay;
            set => _client.NoDelay = value;
        }

        #region 心跳
        /// <summary>
        /// 读取或设置心跳包间隔（毫秒，0 则不发送心跳包）
        /// </summary>
        public uint KeepAliveTime
        {
            get => _client.KeepAliveTime;
            set => _client.KeepAliveTime = value;
        }
        /// <summary>
        /// 读取或设置心跳确认包检测间隔（毫秒，0 不发送心跳包，如果超过若干次 [默认：WinXP 5 次, Win7 10 次] 检测不到心跳确认包则认为已断线）
        /// </summary>
        public uint KeepAliveInterval
        {
            get => _client.KeepAliveInterval;
            set => _client.KeepAliveInterval = value;
        }
        #endregion 心跳

        /// <summary>
        /// 获取或设置暂停接收状态，设置状态时，不允许设置为ReceiveState.Unknown，
        /// </summary>
        public ReceiveState PauseReceive
        {
            get => _client.PauseReceive;
            set => _client.PauseReceive = value;
        }

        /// <summary>
        /// 获取或设置地址重用选项
        /// </summary>
        public ReuseAddressPolicy ReuseAddressPolicy
        {
            get => _client.ReuseAddressPolicy;
            set => _client.ReuseAddressPolicy = value;
        }

        /// <summary>
        /// 代理列表
        /// </summary>
        public List<IProxy> ProxyList
        {
            get => _client.ProxyList;
            set => _client.ProxyList = value;
        }

        #region 性能优化
        /// <summary>
        /// 读取或设置内存块缓存池大小（通常设置为 -> PUSH 模型：5 - 10；PULL 模型：10 - 20 ）
        /// </summary>
        public uint FreeBufferPoolSize
        {
            get => _client.FreeBufferPoolSize;
            set => _client.FreeBufferPoolSize = value;
        }

        /// <summary>
        /// 读取或设置内存块缓存池回收阀值（通常设置为内存块缓存池大小的 3 倍）
        /// </summary>
        public uint FreeBufferPoolHold
        {
            get => _client.FreeBufferPoolHold;
            set => _client.FreeBufferPoolHold = value;
        }
        #endregion 性能优化
        #endregion 创建时的设置项

        #region 常用属性
        /// <summary>
        /// 检查通信组件是否已启动
        /// </summary>
        public bool HasStarted => _client.HasStarted;

        /// <summary>
        /// 状态
        /// </summary>
        public ServiceState State => _client.State;

        /// <summary>
        /// 是否已连接
        /// </summary>
        public bool IsConnected => _client.IsConnected;

        /// <summary>
        /// 是否为安全连接（SSL/HTTPS）
        /// </summary>
        public bool IsSecure => _client.IsSecure;

        /// <summary>
        /// 获取该组件对象的连接Id
        /// </summary>
        public IntPtr ConnectionId => _client.ConnectionId;
        #endregion 常用属性

        #region 常用方法
        #region 启动并连接到服务器
        /// <summary>
        /// 启动通讯组件并连接到服务器
        /// </summary>
        /// <returns></returns>
        public bool Connect() => _client.Connect();

        /// <summary>
        /// 启动通讯组件并连接到服务器
        /// </summary>
        /// <param name="address">远程服务器地址</param>
        /// <param name="port">远程服务器端口</param>
        /// <returns></returns>
        public bool Connect(string address, ushort port) => _client.Connect(address, port);
        #endregion 启动并连接到服务器

        /// <summary>
        /// 停止服务
        /// </summary>
        /// <returns></returns>
        public bool Stop() => _client.Stop();

        #region 发送数据
        /// <summary>
        /// 发送数据
        /// </summary>
        /// <param name="bytes">数据包</param>
        /// <param name="length">数据包长度</param>
        /// <returns></returns>
        public bool Send(byte[] bytes, int length) => _client.Send(bytes, length);

        /// <summary>
        /// 发送数据
        /// </summary>
        /// <param name="bytes">数据包</param>
        /// <param name="offset">针对bytes的偏移</param>
        /// <param name="length">数据包长度</param>
        /// <returns></returns>
        public bool Send(byte[] bytes, int offset, int length) => _client.Send(bytes, offset, length);

        /// <summary>
        /// 发送多组数据 向指定连接发送多组数据 TCP - 顺序发送所有数据包
        /// </summary>
        /// <param name="buffers">发送缓冲区数组</param>
        /// <param name="count">发送缓冲区数目</param>
        /// <returns>true.成功,false.失败，可通过 SYSGetLastError() 获取 Windows 错误代码</returns>
        public bool SendPackets(Wsabuf[] buffers, int count) => _client.SendPackets(buffers, count);
        #endregion 发送数据

        #region 发送本地小文件
        /// <summary>
        /// 发送本地小文件
        //  向指定连接发送 4096 KB 以下的小文件
        /// </summary>
        /// <param name="filePath">文件路径</param>
        /// <param name="head">头部附加数据</param>
        /// <param name="tail">尾部附加数据</param>
        /// <returns>true.成功,false.失败，可通过 SYSGetLastError() 获取 Windows 错误代码</returns>
        public bool SendSmallFile(string filePath, ref Wsabuf head, ref Wsabuf tail) => _client.SendSmallFile(filePath, ref head, ref tail);

        /// <summary>
        /// 发送本地小文件
        /// 向指定连接发送 4096 KB 以下的小文件
        /// </summary>
        /// <param name="filePath">文件路径</param>
        /// <param name="head">头部附加数据,可以为null</param>
        /// <param name="tail">尾部附加数据,可以为null</param>
        /// <returns>true.成功,false.失败，可通过 SYSGetLastError() 获取 Windows 错误代码</returns>
        public bool SendSmallFile(string filePath, byte[] head, byte[] tail) => _client.SendSmallFile(filePath, head, tail);
        #endregion 发送本地小文件

        #region 查询连接信息
        /// <summary>
        /// 获取监听socket的地址信息
        /// </summary>
        /// <param name="host"></param>
        /// <param name="port"></param>
        /// <returns></returns>
        public bool GetListenAddress(out string host, out ushort port) => _client.GetListenAddress(out host, out port);

        /// <summary>
        /// 获取连接的远程主机信息
        /// </summary>
        /// <param name="host"></param>
        /// <param name="port"></param>
        /// <returns></returns>
        public bool GetRemoteHost(out string host, out ushort port) => _client.GetRemoteHost(out host, out port);

        /// <summary>
        /// 获取连接中未发出数据的长度
        /// </summary>
        /// <param name="length"></param>
        /// <returns></returns>
        public bool GetPendingDataLength(out int length) => _client.GetPendingDataLength(out length);
        #endregion 查询连接信息

        /// <summary>
        /// 等待代理结果
        /// </summary>
        /// <returns>连接成功返回true, 连接失败返回false</returns>
        public async Task<bool> WaitProxyAsync() => await _client.WaitProxyAsync();
        #endregion 常用方法

        #region 创建TCPClient并连接示例    
        /// <summary>
        /// 创建TCP侦听 -示例
        /// </summary>
        /// <param name="localAddr">地址</param>
        /// <param name="port">端口</param>
        /// <returns></returns>
        public ITcpClient CreateTcpServerDemo(string localAddr, int port)
        {
            // <2>配置TCPServer的参数
            _client.Address = localAddr;      // IP
            _client.Port = (ushort)port;      // 端口
            _client.SocketBufferSize = 4096;  // 缓存4K
            //_client.BindAddress = "";  // 本地绑定到哪个ip
            //_client.BindPort = 0;      // 本地绑定到哪个端口

            // <3> 绑定事件
            //event ClientPrepareConnectEventHandler OnPrepareConnect;  // 准备连接了事件
            //event ClientConnectEventHandler OnConnect;      // 连接事件
            //event ClientHandShakeEventHandler OnHandShake;  // TCP握手成功事件
            //event ClientSendEventHandler OnSend;            // 数据包发送事件
            //event ClientReceiveEventHandler OnReceive;      // 数据包到达事件
            //event ClientCloseEventHandler OnClose;          // TCP连接关闭事件
            _client.OnPrepareConnect += OnPrepareConnect;  // 准备连接了事件
            _client.OnConnect += OnConnect;                // 连接事件
            _client.OnHandShake += OnHandShake;            // TCP握手成功事件
            _client.OnSend += OnSend;                      // 数据包发送事件
            _client.OnReceive += OnReceive;                // 数据包到达事件
            _client.OnClose += OnClose;                    // TCP连接关闭事件

            //_client.Connect();  // 连接
            return _client;
        }
        #region 事件
        /// <summary>
        /// 准备连接了事件
        /// </summary>
        /// <param name="sender">客户端</param>
        /// <param name="socket">客户端Id</param>
        /// <returns></returns>
        private HandleResult OnPrepareConnect(IClient sender, IntPtr socket)
        {
            sender.ExtraData = string.Empty;  // 设置附加数据（用来做粘包处理）

            return HandleResult.Ok;
        }
        /// <summary>
        /// 连接事件
        /// </summary>
        /// <param name="sender">客户端</param>
        /// <returns></returns>
        private HandleResult OnConnect(IClient sender)
        {
            //ShowLog(string.Format("TCP客户端([{0}]{1}:{2})接入TCP服务器{3}:{4}", sender.ConnectionId, sender.BindAddress, sender.BindPort, sender.Address, sender.Port));
            return HandleResult.Ok;
        }
        /// <summary>
        /// TCP握手成功事件
        /// </summary>
        /// <param name="sender">客户端</param>
        /// <returns></returns>
        private HandleResult OnHandShake(IClient sender)
        {
            // 一般用不到
            return HandleResult.Ok;
        }
        /// <summary>
        /// 数据包发送事件
        /// </summary>
        /// <param name="sender">客户端</param>
        /// <param name="data">数据</param>
        /// <returns></returns>
        private HandleResult OnSend(IClient sender, byte[] data)
        {
            //ShowLog(string.Format("TCP客户端发送数据到TCP服务器[{0}]{1}:{2};数据内容[长度{3}]：{4}", sender.ConnectionId, sender.Address, sender.Port, data.Length, Encoding.UTF8.GetString(data)));
            return HandleResult.Ok;
        }
        /// <summary>
        /// 数据包到达事件
        /// </summary>
        /// <param name="sender">客户端</param>
        /// <param name="data">数据</param>
        /// <returns></returns>
        private HandleResult OnReceive(IClient sender, byte[] data)
        {
            // <1> 获取附加数据对象
            if (!(sender.ExtraData is string extraDataStr))
            {
                return HandleResult.Error;
            }

            // <2> 将接收数据转换成字符串
            string msg = Encoding.UTF8.GetString(data);
            extraDataStr += msg;  // 添加数据到缓存_不合格的数据添加到缓存区（用于粘包、拆包）

            // <3> 显示信息
            //ShowLog(string.Format("TCP客户端接收到TCP服务器[ID:{0},IP:‘{1}:{2}’]的信息;数据:[长度{3}]: {4}", sender.ConnectionId, sender.Address, sender.Port, data.Length, msg));

            // <4> 处理数据
            HandleResult result;
            string _endsWith = "\r\n";
            while (true)
            {
                int index = extraDataStr.IndexOf(_endsWith);
                if (index == -1) // 数据接收不完整，忽略后等待下一次接收
                {
                    result = HandleResult.Ignore;
                    break;
                }
                else
                {
                    string validMsg = extraDataStr.Remove(0, index);
                    // <6> 业务处理-异步(validMsg内容解析时的格式校验属于业务范畴)

                    // <7> 移除已取出数据
                    extraDataStr = extraDataStr.Remove(0, index + _endsWith.Length);
                }
            }

            // <5> 保存PacketData数据
            if (extraDataStr.Length > _client.SocketBufferSize)  // 可选-控制长度为4096(注：实际所占空间大小>4096)
            {
                int length_Delete = extraDataStr.Length - Convert.ToInt32(_client.SocketBufferSize);
                extraDataStr = extraDataStr.Remove(0, length_Delete);  // 清除长度超标数据
            }
            sender.ExtraData = extraDataStr;  // 初始化附加信息

            return result;
        }
        /// <summary>
        /// TCP连接关闭事件
        /// </summary>
        /// <param name="sender">客户端</param>
        /// <param name="socketOperation">关闭类型</param>
        /// <param name="errorCode">错误代码</param>
        /// <returns></returns>
        private HandleResult OnClose(IClient sender, SocketOperation socketOperation, int errorCode)
        {
            sender.ExtraData = null;  // 删除附加数据
            //ShowLog(string.Format("TCP客户端断开与TCP服务器[{0}] {1}:{2}的连接, 断开类型: {3},错误代码: {4}", sender.ConnectionId, sender.Address, sender.Port, socketOperation.ToString(), errorCode));
            return HandleResult.Ok;
        }
        #endregion 事件
        #endregion 创建TCPClient并连接示例
        #endregion 发送端（客户端）
    }
}